/*
 * Project: Set Sample
 * Copyright (C) 2013 alf.labs gmail com,
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.alflabs.setsample.widgets;

import android.content.Context;
import android.graphics.*;
import android.util.AttributeSet;
import com.alflabs.setsample.model.SetCard;
import com.alflabs.utils.RSparseArray;
import android.widget.CheckBox;
import com.alflabs.setsample.R;

public class SetCheckButton extends CheckBox {

    private static final String TAG = SetCheckButton.class.getSimpleName();

    private SetCard mCard;

    // By default draw at a size that works well for a GN, our target device. We'll rescale for others.
    private static final float PRECOMPUTED_WIDTH = 114.8f;

    static class PaintInfo {
        Paint mPaint1;
        Paint mPaint2;
        Path  mPath;
        RectF mRectF;
    }

    private static RSparseArray<PaintInfo> sPaintInfos = new RSparseArray<PaintInfo>(81);


    public SetCheckButton(Context context) {
        super(context);
    }

    public SetCheckButton(Context context, AttributeSet attrs) {
        this(context, attrs, R.style.SetButtonStyle);
    }

    public SetCheckButton(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    /** Force layout to fixed ratio. */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // To force a fixed ration, let the parent determine width/height
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        // then adjust the height to match the width and the ratio
        int mw = getMeasuredWidth();
        //int mh = getMeasuredHeight();
        setMeasuredDimension(mw, (mw * 3)/ 2);
    }

    public void setCard(SetCard card) {
        mCard = card;
        initPaint();
        invalidate();
    }

    public SetCard getCard() {
        return mCard;
    }

    private void initPaint() {
        int h = mCard.hashCode();
        PaintInfo info = sPaintInfos.get(h);
        if (info != null) {
            return;
        }

        info = new PaintInfo();
        sPaintInfos.put(h, info);

        int col = 0;
        switch (mCard.getColor()) {
        case SetCard.COLOR_RED:    col = SetCard.RGB_COLOR_RED   ; break;
        case SetCard.COLOR_GREEN:  col = SetCard.RGB_COLOR_GREEN ; break;
        case SetCard.COLOR_PURPLE: col = SetCard.RGB_COLOR_PURPLE; break;
        default:
            return;
        }

        info.mPaint1 = new Paint();
        info.mPaint1.setColor(col);
        info.mPaint1.setAntiAlias(true);
        info.mPaint1.setStrokeJoin(Paint.Join.ROUND);

        switch (mCard.getPattern()) {
        case SetCard.PATTERN_SOLID:
            info.mPaint1.setStyle(Paint.Style.FILL_AND_STROKE);
            break;

        case SetCard.PATTERN_OUTLINE:
            info.mPaint1.setStyle(Paint.Style.STROKE);
            break;

        case SetCard.PATTERN_STRIPPED:
            info.mPaint1.setStyle(Paint.Style.STROKE);

            info.mPaint2 = new Paint(info.mPaint1);
            info.mPaint2.setStyle(Paint.Style.FILL);
            int tr = col & 0x00FFFFFF;
            Shader s = new LinearGradient(
                    0, 0, 0, 6,
                    new int[] { tr, tr, tr, tr, col, col, tr, },
                    null,
                    Shader.TileMode.MIRROR);
            info.mPaint2.setShader(s);
            break;
        }

        final float cx = 0;
        final float cy = 0;
        final float sx = PRECOMPUTED_WIDTH * 8 / 10;
        final float sy = sx * 3 / 2 / 3;
        final float sx2 = sx/2;
        final float sy2 = sy/2;
        final float sx4 = sx/4;
        final float sy4 = sy/4;
        Path p;
        switch(mCard.getShape()) {
        case SetCard.SHAPE_OVAL:
            info.mRectF = new RectF(cx - sx2, cy - sy2,  cx + sx2, cy + sy2);
            break;

        case SetCard.SHAPE_DIAMOND:
            p = info.mPath = new Path();
            p.moveTo(cx    , cy-sy2);     // p0
            p.lineTo(cx+sx2, cy    );     // p1
            p.lineTo(cx    , cy+sy2);     // p2
            p.lineTo(cx-sx2, cy    );     // p3
            p.close();
            break;

        case SetCard.SHAPE_SQUIGGLE:
            p = info.mPath = new Path();
            p.moveTo(cx, cy - sy4);                                              // p0
            p.cubicTo(cx+sx4, cy-sy4,   cx+sx4, cy-sy2-sy4,   cx+sx2, cy-sy2);   // ->p1
            p.cubicTo(cx+sx2, cy    ,   cx+sx4, cy+sy4,       cx,     cy+sy4);   // ->p2
            p.cubicTo(cx-sx4, cy+sy4,   cx-sx4, cy+sy2+sy4,   cx-sx2, cy+sy2);   // ->p3
            p.cubicTo(cx-sx2, cy    ,   cx-sx4, cy-sy4    ,   cx    , cy-sy4);   // ->p0
            p.close();
            break;
        }

        info.mPaint1.setStrokeWidth(sx / 20);
        if (info.mPaint2 != null) {
            info.mPaint2.setStrokeWidth(sx / 20);
        }

    }

    @Override
    public void draw(@SuppressWarnings("NullableProblems") Canvas canvas) {
        super.draw(canvas);

        if (mCard == null) return;

        PaintInfo info = sPaintInfos.get(mCard.hashCode());
        if (info == null) return;
        if (info.mPaint1 == null) return;

        final float sx = this.getWidth();
        final float sy = this.getHeight();
        final float sx2 = sx / 2;
        final float sy2 = sy / 2;
        final float sx3 = sx * 7 / 10;
        final float sy3 = sy * 7 / 10 / 3;

        switch (mCard.getNumber()) {
        case SetCard.NUMBER_1:
            drawShape(info, canvas, sx3, sy3, sx2, sy2);
            break;
        case SetCard.NUMBER_2:
            drawShape(info, canvas, sx3, sy3, sx2, sy2 - sy3 / 2);
            drawShape(info, canvas, sx3, sy3, sx2, sy2 + sy3 / 2);
            break;
        case SetCard.NUMBER_3:
            drawShape(info, canvas, sx3, sy3, sx2, sy2      );
            drawShape(info, canvas, sx3, sy3, sx2, sy2 - sy3);
            drawShape(info, canvas, sx3, sy3, sx2, sy2 + sy3);
            break;
        }
    }

    private void drawShape(PaintInfo info, Canvas canvas, float sx, float sy, float cx, float cy) {

        canvas.save(Canvas.MATRIX_SAVE_FLAG);
        canvas.translate(cx, cy);
        float scaling = sx / PRECOMPUTED_WIDTH;
        canvas.scale(scaling, scaling);

        Path p = info.mPath;    // can be null

        switch(mCard.getShape()) {
        case SetCard.SHAPE_OVAL:
            RectF r = info.mRectF;
            if (r != null) {
                canvas.drawOval(r, info.mPaint1);
                if (info.mPaint2 != null) {
                    canvas.drawOval(r, info.mPaint2);
                }
            }
            break;

        case SetCard.SHAPE_DIAMOND:
        case SetCard.SHAPE_SQUIGGLE:
            if (p != null) {
                canvas.drawPath(p, info.mPaint1);
                if (info.mPaint2 != null) {
                    canvas.drawPath(p, info.mPaint2);
                }
            }
            break;
        }

        canvas.restore();
    }
}
